package TXPathFinder;

import java.awt.geom.Point2D;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Geometry;
import javax.media.j3d.LineArray;
import javax.media.j3d.Material;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.vecmath.Color3f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

/**
*       \class	ShapeService
*	\brief	Classe de service qui permet de créer tout les élément du décor 3D (les murs, la map ...)
**/
public class ShapeService {

    private static int longueur;
    private static int largeur;
    private static BranchGroup BGtemp;

    /**
    *	\brief	Crée la base de la map, en faite les 4 murs, pas de toit et le sol est crée dans une autre fonction
    *   \param longueur Longueur de la map
    *   \param largeur Largeur de la map
    *   \param hauteur Heuteur de la map
    *   \param color Couleur de la map
    *   \return La shape3D à inserer dans la viewer3D
    **/
    public static Shape3D housse(float longueur, float largeur, float hauteur, Color3f color)
    {
        QuadArray quad=new QuadArray(32,QuadArray.COORDINATES|QuadArray.COLOR_3|QuadArray.NORMALS);
        int i;
        //face droite
        quad.setCoordinate(3,new Point3f(1.0f*longueur,0.0f, 1.0f*hauteur));
        quad.setCoordinate(2,new Point3f(1.0f*longueur, 0.0f,0.0f));
        quad.setCoordinate(1,new Point3f(1.0f*longueur, 1.0f*largeur, 0.0f));
        quad.setCoordinate(0,new Point3f(1.0f*longueur, 1.0f*largeur, 1.0f*hauteur));
        //normal
        Vector3f normal3 = new Vector3f (1.0f, 0, 0);
        for (i=0; i<3; i++) quad.setNormal (i, normal3);
        //face gauche
        quad.setCoordinate(7,new Point3f(0.0f, 0.0f, 0.0f));
        quad.setCoordinate(6,new Point3f(0.0f, 0.0f, 1.0f*hauteur));
        quad.setCoordinate(5,new Point3f(0.0f, 1.0f*largeur, 1.0f*hauteur));
        quad.setCoordinate(4,new Point3f(0.0f, 1.0f*largeur, 0.0f));
        //normal
        Vector3f normal4 = new Vector3f (-1.0f, 0, 0);
        for (i=4; i<8; i++) quad.setNormal (i, normal4);
        //Face haut
        quad.setCoordinate(11,new Point3f(0.0f, 1.0f*largeur, 1.0f*hauteur));
        quad.setCoordinate(10,new Point3f(1.0f*longueur, 1.0f*largeur, 1.0f*hauteur));
        quad.setCoordinate(9,new Point3f(1.0f*longueur, 1.0f*largeur, 0.0f));
        quad.setCoordinate(8,new Point3f(0.0f, 1.0f*largeur, 0.0f));
        //normal
        Vector3f normal5 = new Vector3f (0, 1.f, 0);
        for (i=8; i<12; i++) quad.setNormal (i, normal5);
        //face bas
        quad.setCoordinate(15,new Point3f(0.0f, 0.0f, 0.0f));
        quad.setCoordinate(14,new Point3f(1.0f*longueur, 0.0f, 0.0f));
        quad.setCoordinate(13,new Point3f(1.0f*longueur, 0.0f, 1.0f*hauteur));
        quad.setCoordinate(12,new Point3f(0.0f, 0.0f, 1.0f*hauteur));
        //normal
        Vector3f normal6 = new Vector3f (0, -1.f, 0);
        for (i=12; i<16; i++) quad.setNormal (i, normal6);

        //face droite
        quad.setCoordinate(16,new Point3f(1.0f*longueur,0.0f, 1.0f*hauteur));
        quad.setCoordinate(17,new Point3f(1.0f*longueur, 0.0f,0.0f));
        quad.setCoordinate(18,new Point3f(1.0f*longueur, 1.0f*largeur, 0.0f));
        quad.setCoordinate(19,new Point3f(1.0f*longueur, 1.0f*largeur, 1.0f*hauteur));
        //normal
        Vector3f normal32 = new Vector3f (1.0f, 0, 0);
        for (i=16; i<20; i++) quad.setNormal (i, normal32);
        //face gauche
        quad.setCoordinate(20,new Point3f(0.0f, 0.0f, 0.0f));
        quad.setCoordinate(21,new Point3f(0.0f, 0.0f, 1.0f*hauteur));
        quad.setCoordinate(22,new Point3f(0.0f, 1.0f*largeur, 1.0f*hauteur));
        quad.setCoordinate(23,new Point3f(0.0f, 1.0f*largeur, 0.0f));
        //normal
        Vector3f normal42 = new Vector3f (-1.0f, 0, 0);
        for (i=20; i<24; i++) quad.setNormal (i, normal42);
        //Face haut
        quad.setCoordinate(24,new Point3f(0.0f, 1.0f*largeur, 1.0f*hauteur));
        quad.setCoordinate(25,new Point3f(1.0f*longueur, 1.0f*largeur, 1.0f*hauteur));
        quad.setCoordinate(26,new Point3f(1.0f*longueur, 1.0f*largeur, 0.0f));
        quad.setCoordinate(27,new Point3f(0.0f, 1.0f*largeur, 0.0f));
        //normal
        Vector3f normal52 = new Vector3f (0, 1.f, 0);
        for (i=24; i<28; i++) quad.setNormal (i, normal52);
        //face bas
        quad.setCoordinate(28,new Point3f(0.0f, 0.0f, 0.0f));
        quad.setCoordinate(29,new Point3f(1.0f*longueur, 0.0f, 0.0f));
        quad.setCoordinate(30,new Point3f(1.0f*longueur, 0.0f, 1.0f*hauteur));
        quad.setCoordinate(31,new Point3f(0.0f, 0.0f, 1.0f*hauteur));
        //normal
        Vector3f normal62 = new Vector3f (0, -1.f, 0);
        for (i=28; i<32; i++) quad.setNormal (i, normal62);

        for(int j=0;j<32;j++) quad.setColor(j,color);

        //apparence of the room
        Appearance app=new Appearance();
        app.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_READ);
        app.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
        Material mat = new Material();
        mat.setDiffuseColor(color.getX(),color.getY(),color.getZ(),1.0f);
        app.setMaterial (mat);

        Shape3D shape=new Shape3D(quad,app);
        return shape;
    }

    /**
    *	\brief	Crée les murs de la map , utilie aussi pour crée les ghosts
    *   \param longueur Longueur du mur
    *   \param largeur Largeur du mur
    *   \param hauteur Heuteur du mur
    *   \param color Couleur du mur
    *   \param xPos Position x du mur
    *   \param yPox Position y du mur
    *   \param larg Largeur de la map
    *   \param transparent Transparence du mur
    *   \return Le groupe à inserer dans la viewer3D contenant le mur
    **/
    public static TransformGroup bloc(float longueur, float largeur, float hauteur, Color3f color, int xPos, int yPos, int larg, boolean transparent)
    {

        QuadArray quad=new QuadArray(48,QuadArray.COORDINATES|QuadArray.COLOR_3|QuadArray.NORMALS);
        int i;
        //face avant
        quad.setCoordinate(3,new Point3f(0.0f,0.0f, 1.0f*largeur));
        quad.setCoordinate(2,new Point3f(1.0f*longueur,0.0f, 1.0f*largeur));
        quad.setCoordinate(1,new Point3f(1.0f*longueur, 1.0f*hauteur,1.0f*largeur));
        quad.setCoordinate(0,new Point3f(0.0f, 1.0f*hauteur, 1.0f*largeur));
        //normal
        Vector3f normal1 = new Vector3f (0, 0, 1.f);
        for (i=0; i<4; i++) quad.setNormal (i, normal1);
        //face arriere
        quad.setCoordinate(7,new Point3f(1.0f*longueur,0.0f,0.0f));
        quad.setCoordinate(6,new Point3f(0.0f,0.0f,0.0f));
        quad.setCoordinate(5,new Point3f(0.0f, 1.0f*hauteur, 0.0f));
        quad.setCoordinate(4,new Point3f(1.0f*longueur, 1.0f*hauteur, 0.0f));
        //normal
        Vector3f normal2 = new Vector3f (0, 0, -1.f);
        for (i=4; i<8; i++) quad.setNormal (i, normal2);
        //face droite
        quad.setCoordinate(11,new Point3f(1.0f*longueur,0.0f, 1.0f*largeur));
        quad.setCoordinate(10,new Point3f(1.0f*longueur,0.0f,0.0f));
        quad.setCoordinate(9,new Point3f(1.0f*longueur, 1.0f*hauteur,0.0f));
        quad.setCoordinate(8,new Point3f(1.0f*longueur, 1.0f*hauteur, 1.0f*largeur));
        //normal
        Vector3f normal3 = new Vector3f (1.0f, 0, 0);
        for (i=8; i<12; i++) quad.setNormal (i, normal3);
        //face gauche
        quad.setCoordinate(15,new Point3f(0.0f,0.0f,0.0f));
        quad.setCoordinate(14,new Point3f(0.0f, 0.0f, 1.0f*largeur));
        quad.setCoordinate(13,new Point3f(0.0f, 1.0f*hauteur, 1.0f*largeur));
        quad.setCoordinate(12,new Point3f(0.0f, 1.0f*hauteur, 0.0f));
        //normal
        Vector3f normal4 = new Vector3f (-1.0f, 0, 0);
        for (i=12; i<16; i++) quad.setNormal (i, normal4);
        //Face haut
        quad.setCoordinate(19,new Point3f(0.0f, 1.0f*hauteur, 1.0f*largeur));
        quad.setCoordinate(18,new Point3f(1.0f*longueur, 1.0f*hauteur, 1.0f*largeur));
        quad.setCoordinate(17,new Point3f(1.0f*longueur, 1.0f*hauteur, 0.0f));
        quad.setCoordinate(16,new Point3f(0.0f, 1.0f*hauteur, 0.0f));
        //normal
        Vector3f normal5 = new Vector3f (0, 1.f, 0);
        for (i=16; i<20; i++) quad.setNormal (i, normal5);
        //face bas
        quad.setCoordinate(23,new Point3f(0.0f, 0.0f, 0.0f));
        quad.setCoordinate(22,new Point3f(1.0f*longueur, 0.0f, 0.0f));
        quad.setCoordinate(21,new Point3f(1.0f*longueur, 0.0f, 1.0f*largeur));
        quad.setCoordinate(20,new Point3f(0.0f, 0.0f, 1.0f*largeur));
        //normal
        Vector3f normal6 = new Vector3f (0, -1.f, 0);
        for (i=20; i<24; i++) quad.setNormal (i, normal6);

        //face avant
        quad.setCoordinate(24,new Point3f(0.0f,0.0f, 1.0f*largeur));
        quad.setCoordinate(25,new Point3f(1.0f*longueur,0.0f, 1.0f*largeur));
        quad.setCoordinate(26,new Point3f(1.0f*longueur, 1.0f*hauteur,1.0f*largeur));
        quad.setCoordinate(27,new Point3f(0.0f, 1.0f*hauteur, 1.0f*largeur));
        //normal
        Vector3f normal12 = new Vector3f (0, 0, 1.f);
        for (i=24; i<28; i++) quad.setNormal (i, normal12);
        //face arriere
        quad.setCoordinate(28,new Point3f(1.0f*longueur,0.0f, 0.0f));
        quad.setCoordinate(29,new Point3f(0.0f,0.0f,0.0f));
        quad.setCoordinate(30,new Point3f(0.0f, 1.0f*hauteur, 0.0f));
        quad.setCoordinate(31,new Point3f(1.0f*longueur, 1.0f*hauteur, 0.0f));
        //normal
        Vector3f normal22 = new Vector3f (0, 0, -1.f);
        for (i=28; i<32; i++) quad.setNormal (i, normal22);
        //face droite
        quad.setCoordinate(32,new Point3f(1.0f*longueur,0.0f, 1.0f*largeur));
        quad.setCoordinate(33,new Point3f(1.0f*longueur, 0.0f,0.0f));
        quad.setCoordinate(34,new Point3f(1.0f*longueur, 1.0f*hauteur, 0.0f));
        quad.setCoordinate(35,new Point3f(1.0f*longueur, 1.0f*hauteur, 1.0f*largeur));
        //normal
        Vector3f normal32 = new Vector3f (1.0f, 0, 0);
        for (i=32; i<36; i++) quad.setNormal (i, normal32);
        //face gauche
        quad.setCoordinate(36,new Point3f(0.0f, 0.0f, 0.0f));
        quad.setCoordinate(37,new Point3f(0.0f, 0.0f, 1.0f*largeur));
        quad.setCoordinate(38,new Point3f(0.0f, 1.0f*hauteur, 1.0f*largeur));
        quad.setCoordinate(39,new Point3f(0.0f, 1.0f*hauteur, 0.0f));
        //normal
        Vector3f normal42 = new Vector3f (-1.0f, 0, 0);
        for (i=36; i<40; i++) quad.setNormal (i, normal42);
        //Face haut
        quad.setCoordinate(40,new Point3f(0.0f, 1.0f*hauteur, 1.0f*largeur));
        quad.setCoordinate(41,new Point3f(1.0f*longueur, 1.0f*hauteur, 1.0f*largeur));
        quad.setCoordinate(42,new Point3f(1.0f*longueur, 1.0f*hauteur, 0.0f));
        quad.setCoordinate(43,new Point3f(0.0f, 1.0f*hauteur, 0.0f));
        //normal
        Vector3f normal52 = new Vector3f (0, 1.f, 0);
        for (i=40; i<44; i++) quad.setNormal (i, normal52);
        //face bas
        quad.setCoordinate(44,new Point3f(0.0f, 0.0f, 0.0f));
        quad.setCoordinate(45,new Point3f(1.0f*longueur, 0.0f, 0.0f));
        quad.setCoordinate(46,new Point3f(1.0f*longueur, 0.0f, 1.0f*largeur));
        quad.setCoordinate(47,new Point3f(0.0f, 0.0f, 1.0f*largeur));

        //normal
        Vector3f normal62 = new Vector3f (0, -1.f, 0);
        for (i=44; i<48; i++) quad.setNormal (i, normal62);

        for(int j=0;j<48;j++) quad.setColor(j,color);

        //apparence of the room
        Appearance app=new Appearance();
        Material mat = new Material();
        app.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_READ);
        app.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
        mat.setDiffuseColor(color.getX(),color.getY(),color.getZ(),1.0f);
        app.setMaterial (mat);
        if( transparent )
        {
            TransparencyAttributes ta = new TransparencyAttributes();
            ta.setTransparencyMode (ta.BLENDED);
            ta.setTransparency (0.75f);
            app.setTransparencyAttributes(ta);
        }

        //quad.setCapability(QuadArray.ENABLE_APPEARANCE_MODIFY);
        Shape3D shape=new Shape3D(quad,app);
        shape.setCapability(shape.ALLOW_APPEARANCE_READ);
        shape.setCapability(shape.ALLOW_APPEARANCE_WRITE);

        TransformGroup TG = new TransformGroup();
        Transform3D trans=new Transform3D();
        Vector3f translation=new Vector3f();
        translation.set(xPos-1,yPos-1,0.0f);
        trans.setTranslation(translation);
        TG.setTransform(trans);

        String key = Integer.toString(xPos - 1)+  ":" +Integer.toString((int) (larg - yPos));
        //System.out.println("key : " + key);
        DataModel.getInstance().addShapeTable(key , shape);
        TG.addChild(shape);
        return TG;
        //return shape;
    }

    /**
    *	\brief	Crée le sol de la map, le sol est divisé en carré de façon à pouvoir selectionner chacun d'entres eux.
    *   \param longueur Longueur du mur
    *   \param largeur Largeur du mur
    *   \param hauteur Heuteur du mur
    *   \param color Couleur du mur
    *   \param xPos Position x du mur
    *   \param yPox Position y du mur
    *   \param larg Largeur de la map
    *   \return Le groupe à inserer dans la viewer3D contenant le mur
    **/
    public static TransformGroup blocSocle(float longueur, float largeur, float hauteur, Color3f color, int xPos, int yPos, int larg)
    {

        QuadArray quad=new QuadArray(8,QuadArray.COORDINATES|QuadArray.COLOR_3|QuadArray.NORMALS);
        int i;
        //face avant
        quad.setCoordinate(0,new Point3f(0.0f,0.0f, 1.0f*largeur));
        quad.setCoordinate(1,new Point3f(1.0f*longueur,0.0f, 1.0f*largeur));
        quad.setCoordinate(2,new Point3f(1.0f*longueur, 1.0f*hauteur,1.0f*largeur));
        quad.setCoordinate(3,new Point3f(0.0f, 1.0f*hauteur, 1.0f*largeur));
        //normal
        Vector3f normal1 = new Vector3f (0, 0, 1.f);
        for (i=0; i<4; i++) quad.setNormal (i, normal1);
        //face arriere
        quad.setCoordinate(4,new Point3f(1.0f*longueur,0.0f,0.0f));
        quad.setCoordinate(5,new Point3f(0.0f,0.0f,0.0f));
        quad.setCoordinate(6,new Point3f(0.0f, 1.0f*hauteur, 0.0f));
        quad.setCoordinate(7,new Point3f(1.0f*longueur, 1.0f*hauteur, 0.0f));
        //normal
        Vector3f normal2 = new Vector3f (0, 0, -1.f);
        for (i=4; i<8; i++) quad.setNormal (i, normal2);
   

        for(int j=0;j<8;j++) quad.setColor(j,color);

        //apparence of the room
        Appearance app=new Appearance();
        Material mat = new Material();
        app.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_READ);
        app.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
        mat.setDiffuseColor(color.getX(),color.getY(),color.getZ(),1.0f);
        app.setMaterial (mat);

        //quad.setCapability(QuadArray.ENABLE_APPEARANCE_MODIFY);
        Shape3D shape=new Shape3D(quad,app);
        shape.setCapability(shape.ALLOW_APPEARANCE_READ);
        shape.setCapability(shape.ALLOW_APPEARANCE_WRITE);
        shape.setName(Integer.toString(xPos - 1)+  ":" +Integer.toString((int) (larg - yPos)));

        TransformGroup TG = new TransformGroup();
        Transform3D trans=new Transform3D();
        Vector3f translation=new Vector3f();
        translation.set(xPos-1,yPos-1,0.0f);
        trans.setTranslation(translation);
        TG.setTransform(trans);

        //ajout dans la hashtable
        String key = Integer.toString(xPos - 1)+  ":" +Integer.toString((int) (larg - yPos));
        //System.out.println("key : " + key);
        DataModel.getInstance().addShapeTable(key , shape);
        TG.addChild(shape);
        return TG;
        //return shape;
    }

    /**
    *	\brief	Crée la grille de la map3D
    *   \param longueurRoom Longueur de la map
    *   \param largeurRoom Largeur de la map
    *   \return La géometrie de la grille, à inserer dans le viewer3D
    **/
     public static Geometry geomGrille(int longueurRoom, int largeurRoom)
    {
        //ligne
        int nombreLignes = largeurRoom+20;
        int nombreColonne = longueurRoom;
        int compteurLigne = 0;

        LineArray grille=new LineArray((nombreLignes+nombreColonne)*2,LineArray.COORDINATES|LineArray.COLOR_3);
        //ligne verticale
        for( int i = 1 ; i <= nombreLignes-1 ; i++ )
        {
            grille.setCoordinate(compteurLigne,new Point3f(i,0.0f,0.1f));
            compteurLigne++;
            grille.setCoordinate(compteurLigne,new Point3f(i,nombreLignes,0.1f));
            compteurLigne++;
        }
        //ligne horizontale
        for( int i = 1 ; i <= nombreColonne-1 ; i++ )
        {
            grille.setCoordinate(compteurLigne,new Point3f(0.0f,i,0.1f));
            compteurLigne++;
            grille.setCoordinate(compteurLigne,new Point3f(nombreColonne,i,0.1f));
            compteurLigne++;
        }

        return grille;
    }

    /**
    *	\brief	Crée la forme3D d'un ghost
    *   \param x Position x du ghost
    *   \param y Position y du ghost
    *   \return La branche du ghost à inserer dans le viewer 3D
    **/
    public static BranchGroup createGhost(int x, int y)
    {
        BranchGroup bg = new BranchGroup();
        bg.setCapability(BranchGroup.ALLOW_DETACH);
        TransformGroup transG = ShapeService.bloc(0.9f,0.9f,0.9f,new Color3f(0.2f,0.2f,0.4f),x+1,largeur-y,largeur, true);
        bg.addChild(transG);
        return bg;
    }

    /**
    *	\brief	Crée la map à partir du fichier map
    *   \param file Fichier de la map
    *   \return La branche de la map à inserer dans le viewer 3D
    **/
    public static BranchGroup readMap(String file) throws FileNotFoundException, IOException
    {
        DataModel.getInstance().getPf().loadMap(file);
        Map3D map = DataModel.getInstance().getPf().getMap();

        BranchGroup branchG = new BranchGroup();
        BGtemp = new BranchGroup();
        int ligne = map.getRow();
        int colonne = map.getCol();
        longueur = colonne;
        largeur = ligne;

        for( int i = 0 ; i < ligne ; i++)
        {
            for( int j = 0 ; j < colonne ; j++ )
            {
                if( map.getValue(i, j) == 1 )
                {
                    TransformGroup transG = ShapeService.bloc(1,1,1,new Color3f(0.5f,0.5f,0.7f),j+1,ligne-i,largeur, false);
                    branchG.addChild(transG);
                }
                else if ( map.getValue(i, j) == 2 )
                {
                    TransformGroup transG = ShapeService.blocSocle(1, 0.01f, 1, new Color3f(0.0f,0.7f,0.0f),j+1,largeur-i,largeur);
                    BGtemp.addChild(transG);
                }
                else
                {
                    TransformGroup transG = ShapeService.blocSocle(1, 0.01f, 1, new Color3f(0.9f,0.9f,0.9f),j+1,largeur-i,largeur);
                    BGtemp.addChild(transG);
                }
            }
        }

        return branchG;
    }

        /**
    *	\brief	Accesseur
    *   \return Le nombre de colonne de la map
    **/
    public static int getLongueur()
    {
        return longueur;
    }

    /**
    *	\brief	Accesseur
    *   \return Le nombre de ligne de la map
    **/
    public static int getLargeur()
    {
        return largeur;
    }

    /**
    *	\brief	Accesseur
    *   \return La grille qui permet la selection au sol de position dans la map
    **/
    static BranchGroup returnGridForSelection() {

        BranchGroup branchG = BGtemp;
        return branchG;
    }
}
